package org.fastsyncer.core.util;

import java.sql.Timestamp;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.naming.NamingException;

import org.apache.commons.lang.StringUtils;
import org.fastsyncer.common.constant.ConnectorConstant;
import org.fastsyncer.common.constant.MappingConstant;
import org.fastsyncer.common.entity.BaseInfo;
import org.fastsyncer.common.entity.DatabaseConfig;
import org.fastsyncer.common.entity.LdapConfig;
import org.fastsyncer.common.entity.Mapping;
import org.fastsyncer.common.entity.MappingTask;
import org.fastsyncer.common.entity.WebServiceConfig;
import org.fastsyncer.connector.framework.Database;
import org.fastsyncer.connector.ldap.LdapConnector;
import org.fastsyncer.connector.mysql.MysqlConnector;
import org.fastsyncer.connector.oracle.OracleConnector;
import org.fastsyncer.connector.sql.SqlMysqlConnector;
import org.fastsyncer.connector.sql.SqlOracleConnector;
import org.fastsyncer.connector.sqlserver.SqlServerConnector;
import org.json.JSONException;
import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.ldap.core.LdapTemplate;

public abstract class ParseDriverSource extends ParseDriverCommon {

    private static Logger logger = LoggerFactory.getLogger(ParseDriverSource.class);

    /**
     * 解析数据源
     * 
     * @Title: parseDriverSource
     * @Description: 解析数据源
     * @param rel
     * @param mappingTask
     * @return: void
     * @throws JSONException
     * @throws ClassNotFoundException
     * @throws IllegalAccessException
     * @throws InstantiationException
     * @throws NamingException 
     */
    protected static void parse(JSONObject rel, MappingTask mappingTask)
            throws JSONException, InstantiationException, IllegalAccessException, ClassNotFoundException, NamingException {
        // 解析数据源
        JSONObject sourceMapping = rel.getJSONObject("sourceMapping");
        switch (sourceMapping.getString("connector")) {
        case ConnectorConstant.MYSQL_CONNECTOR:
            parseMysqlConnector(rel, mappingTask, sourceMapping);
            break;
        case ConnectorConstant.ORACLE_CONNECTOR:
            parseOracleConnector(rel, mappingTask, sourceMapping);
            break;
        case ConnectorConstant.SQLSERVER_CONNECTOR:
            parseSqlServerConnector(rel, mappingTask, sourceMapping);
            break;
        case ConnectorConstant.SQL_MYSQL_CONNECTOR:
            parseSqlMysqlConnector(rel, mappingTask, sourceMapping);
            break;
        case ConnectorConstant.SQL_ORACLE_CONNECTOR:
            parseSqlOracleConnector(rel, mappingTask, sourceMapping);
            break;
        case ConnectorConstant.WS_CONNECTOR:
            parseWSConnector(rel, mappingTask, sourceMapping);
            break;
        case ConnectorConstant.LDAP_CONNECTOR:
            parseLdapConnector(rel, mappingTask, sourceMapping);
            break;
        default:
            break;
        }
    }

    // 解析source Mysql Connector
    private static void parseMysqlConnector(JSONObject rel, MappingTask mappingTask, JSONObject sourceMapping) throws JSONException {
        // 1.获取数据源配置
        DatabaseConfig config = getDataSource(sourceMapping.getJSONObject("config"));

        // 2.获取连接器
        Database connector = MysqlConnector.getInstance();
        // 获取过滤条件
        String queryFilter = connector.getFilter(mappingTask.getQuery());
        Integer sourceRowCount = connector.getBaseInfoConnector(config, queryFilter).getRowCount();

        // 3.获取同步数据源字段、类型
        List<Map<String, Object>> fileds = parseFields(sourceMapping.getJSONArray("fileds"));

        // 4.设置数据源配置、驱动类型、同步属性、数据量
        Mapping mapping = new Mapping(sourceMapping.getString("name"), config, ConnectorConstant.MYSQL_CONNECTOR, fileds, sourceRowCount);

        // 5.拼接数据源执行命令
        if (fileds != null && !fileds.isEmpty()) {
            Map<String, String> executeCommond = mapping.getExecuteCommond();
            // 清空历史sql
            executeCommond.clear();

            String sourceExecuteCommond = connector.getJdbcSQL(ConnectorConstant.OPERTION_QUERY, config, fileds, queryFilter);
            // 当为数据同步时
            String model = mappingTask.getModel();
            if (MappingConstant.SYNC_INCREMENT.equals(model)) {
                // 获取定时quartz增量语句
                getQuartzExecuteCommond(connector, config, fileds, mappingTask.getPolicy(), executeCommond, sourceExecuteCommond, queryFilter);
            } else if (MappingConstant.SYNC_ALL.equals(model)) {
                executeCommond.put(ConnectorConstant.OPERTION_QUERY, sourceExecuteCommond);
                logger.info("sourceExecuteQueryCommond:" + sourceExecuteCommond);
            }
        }
        mappingTask.setSourceMapping(mapping);
    }

    // 解析source Oracle Connector
    private static void parseOracleConnector(JSONObject rel, MappingTask mappingTask, JSONObject sourceMapping) throws JSONException {
        // 1.获取数据源配置
        DatabaseConfig config = getDataSource(sourceMapping.getJSONObject("config"));

        // 2.获取连接器
        Database connector = OracleConnector.getInstance();
        // 获取过滤条件
        String queryFilter = connector.getFilter(mappingTask.getQuery());
        Integer sourceRowCount = connector.getBaseInfoConnector(config, queryFilter).getRowCount();

        // 3.获取同步数据源字段、类型
        List<Map<String, Object>> fileds = parseFields(sourceMapping.getJSONArray("fileds"));

        // 4.设置数据源配置、驱动类型、同步属性、数据量
        Mapping mapping = new Mapping(sourceMapping.getString("name"), config, ConnectorConstant.ORACLE_CONNECTOR, fileds, sourceRowCount);

        // 5.拼接数据源执行命令
        if (fileds != null && !fileds.isEmpty()) {
            Map<String, String> executeCommond = mapping.getExecuteCommond();
            // 清空历史sql
            executeCommond.clear();

            String sourceExecuteCommond = connector.getJdbcSQL(ConnectorConstant.OPERTION_QUERY, config, fileds, queryFilter);
            // 当为数据同步时
            String model = mappingTask.getModel();
            if (MappingConstant.SYNC_INCREMENT.equals(model)) {
                // 获取定时quartz增量语句
                getQuartzExecuteCommond(connector, config, fileds, mappingTask.getPolicy(), executeCommond, sourceExecuteCommond, queryFilter);
            } else if (MappingConstant.SYNC_ALL.equals(model)) {
                executeCommond.put(ConnectorConstant.OPERTION_QUERY, sourceExecuteCommond);
                logger.info("sourceExecuteQueryCommond:" + sourceExecuteCommond);
            }
        }
        mappingTask.setSourceMapping(mapping);
    }

    // 解析source SqlServer Connector
    private static void parseSqlServerConnector(JSONObject rel, MappingTask mappingTask, JSONObject sourceMapping) throws JSONException {
        // 1.获取数据源配置
        DatabaseConfig config = getDataSource(sourceMapping.getJSONObject("config"));

        // 2.获取数据量
        Database connector = SqlServerConnector.getInstance();
        // 获取过滤条件
        String queryFilter = connector.getFilter(mappingTask.getQuery());
        Integer rowCount = connector.getBaseInfoConnector(config, queryFilter).getRowCount();

        // 3.获取同步数据源字段、类型
        List<Map<String, Object>> fileds = parseFields(sourceMapping.getJSONArray("fileds"));

        // 4.设置数据源配置、驱动类型、同步属性、数据量
        Mapping sourceMappingDatabase = new Mapping(sourceMapping.getString("name"), config, ConnectorConstant.SQLSERVER_CONNECTOR, fileds, rowCount);

        // 5.拼接数据源执行命令
        if (fileds != null && !fileds.isEmpty()) {
            Map<String, String> executeCommond = sourceMappingDatabase.getExecuteCommond();
            // 清空历史sql
            executeCommond.clear();

            String sourceExecuteCommond = connector.getJdbcSQL(ConnectorConstant.OPERTION_QUERY, config, fileds, queryFilter);
            // 当为数据同步时
            String model = mappingTask.getModel();
            if (MappingConstant.SYNC_INCREMENT.equals(model)) {
                // 获取定时quartz增量语句
                getQuartzExecuteCommond(connector, config, fileds, mappingTask.getPolicy(), executeCommond, sourceExecuteCommond, queryFilter);
            } else if (MappingConstant.SYNC_ALL.equals(model)) {
                executeCommond.put(ConnectorConstant.OPERTION_QUERY, sourceExecuteCommond);
                logger.info("sourceExecuteQueryCommond:" + sourceExecuteCommond);
            }
        }
        mappingTask.setSourceMapping(sourceMappingDatabase);
    }

    // 解析source Sql Mysql Connector
    private static void parseSqlMysqlConnector(JSONObject rel, MappingTask mappingTask, JSONObject sourceMappingObj) throws JSONException {
        // 1.获取数据源配置
        DatabaseConfig config = getDataSource(sourceMappingObj.getJSONObject("config"));

        // 2.获取数据量
        Database connector = SqlMysqlConnector.getInstance();
        BaseInfo baseInfo = connector.getBaseInfoConnector(config);
        // 更新sql语句解析出来的列
        config.setColumn(baseInfo.getMetaInfo());

        // 3.获取同步数据源字段、类型
        List<Map<String, Object>> fileds = parseFields(sourceMappingObj.getJSONArray("fileds"));

        // 4.设置数据源配置、驱动类型、同步属性、数据量
        Mapping sourceMapping = new Mapping(sourceMappingObj.getString("name"), config, ConnectorConstant.SQL_MYSQL_CONNECTOR, fileds, baseInfo.getRowCount());

        // 5.拼接数据源执行命令
        if (fileds != null && !fileds.isEmpty()) {
            Map<String, String> executeCommond = sourceMapping.getExecuteCommond();
            executeCommond.clear();

            String sql = config.getSql().toUpperCase();
            // 当为数据同步时
            String model = mappingTask.getModel();
            // 策略
            Map<String, Map<String, String>> policy = mappingTask.getPolicy();
            if (MappingConstant.SYNC_INCREMENT.equals(model) && null != policy) {
                // 获取定时log增量语句
                getLogExecuteCommond(config, policy, executeCommond, sql);

                // 获取定时quartz增量语句
                getQuartzExecuteCommond(connector, config, policy, executeCommond, sql);
            } else if (MappingConstant.SYNC_ALL.equals(model)) {
                StringBuilder builder = new StringBuilder(sql);
                StringBuilder sourceExecuteCommond = connector.getPageSQL(config, builder);
                executeCommond.put(ConnectorConstant.OPERTION_QUERY, sourceExecuteCommond.toString());
                logger.info("sourceExecuteQueryCommond:" + sourceExecuteCommond);
            }
        }
        mappingTask.setSourceMapping(sourceMapping);
    }

    // 解析source Sql Oracle Connector
    private static void parseSqlOracleConnector(JSONObject rel, MappingTask mappingTask, JSONObject sourceMappingObj) throws JSONException {
        // 1.获取数据源配置
        DatabaseConfig config = getDataSource(sourceMappingObj.getJSONObject("config"));

        // 2.获取数据量
        Database connector = SqlOracleConnector.getInstance();
        BaseInfo baseInfo = connector.getBaseInfoConnector(config);
        // 更新sql语句解析出来的列
        config.setColumn(baseInfo.getMetaInfo());

        // 3.获取同步数据源字段、类型
        List<Map<String, Object>> fileds = parseFields(sourceMappingObj.getJSONArray("fileds"));

        // 4.设置数据源配置、驱动类型、同步属性、数据量
        Mapping sourceMapping = new Mapping(sourceMappingObj.getString("name"), config, ConnectorConstant.SQL_ORACLE_CONNECTOR, fileds, baseInfo.getRowCount());

        // 5.拼接数据源执行命令
        if (fileds != null && !fileds.isEmpty()) {
            Map<String, String> executeCommond = sourceMapping.getExecuteCommond();
            executeCommond.clear();

            // 直接sql
            String sql = config.getSql().toUpperCase();
            // 策略
            Map<String, Map<String, String>> policy = mappingTask.getPolicy();
            // 当为数据同步时
            String model = mappingTask.getModel();
            if (MappingConstant.SYNC_INCREMENT.equals(model)) {
                // 获取定时log增量语句
                getLogExecuteCommond(config, policy, executeCommond, sql);

                // 获取定时quartz增量语句
                getQuartzExecuteCommond(connector, config, policy, executeCommond, sql);
            } else if (MappingConstant.SYNC_ALL.equals(model)) {
                StringBuilder builder = new StringBuilder(sql);
                StringBuilder sourceExecuteCommond = connector.getPageSQL(config, builder);
                executeCommond.put(ConnectorConstant.OPERTION_QUERY, sourceExecuteCommond.toString());
                logger.info("sourceExecuteQueryCommond:" + sourceExecuteCommond);
            }
        }
        mappingTask.setSourceMapping(sourceMapping);
    }

    // 解析source WS Connector
    private static void parseWSConnector(JSONObject rel, MappingTask mappingTask, JSONObject sourceMappingObj) throws JSONException {
        // 1.获取数据源配置
        WebServiceConfig sourceWSConfig = getWebService(sourceMappingObj.getJSONObject("config"));

        // 2.获取同步数据源、目标源字段和数据源、目标源字段类型
        List<Map<String, Object>> sourceXmlFileds = parseFields(sourceMappingObj.getJSONArray("fileds"));

        // 3.设置数据源配置、驱动类型、同步属性、数据量
        Mapping sourceMappingXml = new Mapping(sourceMappingObj.getString("name"), sourceWSConfig, ConnectorConstant.WS_CONNECTOR, sourceXmlFileds, 0);
        mappingTask.setSourceMapping(sourceMappingXml);
    }

    // 解析source Ldap Connector
    private static void parseLdapConnector(JSONObject rel, MappingTask mappingTask, JSONObject sourceMappingObj) throws JSONException, NamingException {
        // 1.获取数据源配置
        LdapConfig sourceLdapConfig = getLdap(sourceMappingObj.getJSONObject("config"));

        // 2.获取同步数据源、目标源字段和数据源、目标源字段类型
        List<Map<String, Object>> sourceLdapFileds = parseFields(sourceMappingObj.getJSONArray("fileds"));

        // 3.获取连接器
        LdapConnector ldapConnector = LdapConnector.getInstance();
        LdapTemplate ldapTemplate = ldapConnector.getLdapTemplate(sourceLdapConfig);

        // 4.获取过滤条件
        String sourceExecuteCommond = ldapConnector.getFilter(sourceLdapConfig.getExtendObjects(), mappingTask.getQuery());

        // 5.获取总数
        int rowCount = ldapConnector.getRowCount(ldapTemplate, sourceExecuteCommond);
        // Ldap暂时没有合适的分页查询，所以默认将总数设置为最多执行数
        mappingTask.setMaxTask(rowCount);

        // 6.设置数据源配置、驱动类型、同步属性、数据量
        Mapping sourceMappingLdap = new Mapping(sourceMappingObj.getString("name"), sourceLdapConfig, ConnectorConstant.LDAP_CONNECTOR, sourceLdapFileds, rowCount);

        // 7.拼接数据源执行命令
        sourceMappingLdap.getExecuteCommond().put(ConnectorConstant.OPERTION_QUERY, sourceExecuteCommond);
        logger.info("sourceExecuteQueryCommond:" + sourceExecuteCommond);
        mappingTask.setSourceMapping(sourceMappingLdap);
    }

    /**
     * 获取日志log增量语句
     * @param config
     * @param policy
     * @param sqlQuery
     * @param executeCommond
     */
    private static void getLogExecuteCommond(DatabaseConfig config, Map<String, Map<String, String>> po, Map<String, String> executeCommond, String sqlQuery) {
        Map<String, String> inc = po.get(MappingConstant.POLICY_INCREMENT);
        if (null == inc) {
            return;
        }
        // 排除非log策略
        String model = inc.get("model");
        if (!MappingConstant.POLICY_INCREMENT_MODEL_LOG.equals(model)) {
            return;
        }

        // 检查是否包含where语法
        String where = sqlQuery.contains(" WHERE ") || sqlQuery.contains(" WHERE\n") || sqlQuery.contains("\nWHERE\n") ? " AND " : " WHERE ";
        // 使用表别名生成条件语句,用读取增量数据 WHERE T1.ID = ?
        String sql = new StringBuilder().append(where).append(inc.get("tableLabel")).append(".").append(config.getPrimaryKey()).append(" = ?").toString();
        executeCommond.put(ConnectorConstant.OPERTION_QUERY_LOG, sql);
        logger.info("sourceExecuteQueryLogCommond:" + sql);
    }

    /**
     * 获取定时quartz增量语句
     * @param connector
     * @param sourceConfig
     * @param fileds
     * @param po
     * @param executeCommond
     * @param sourceExecuteCommond
     * @param queryFilter
     */
    private static void getQuartzExecuteCommond(Database connector, DatabaseConfig sourceConfig, List<Map<String, Object>> fileds, Map<String, Map<String, String>> po,
            Map<String, String> executeCommond, String sourceExecuteCommond, String queryFilter) {
        Map<String, String> inc = po.get(MappingConstant.POLICY_INCREMENT);
        if (null == inc) {
            return;
        }
        // 排除非定时策略
        String model = inc.get("model");
        if (!MappingConstant.POLICY_INCREMENT_MODEL_QUARTZ.equals(model)) {
            return;
        }

        // 事件字段,用于区分新增/修改/删除 name=ID, typeName=VARCHAR2, type=12
        HashMap<String, Object> map = new HashMap<String, Object>();
        map.put("name", inc.get("quartzEvent"));
        map.put("typeName", "VARCHAR2");
        map.put("type", 12);
        fileds.add(map);

        // 获取查询语句 SELECT ASD_TEST.ID, ASD_TEST.DATE FROM ASD_TEST WHERE (ID!='123')
        sourceExecuteCommond = connector.getJdbcSQL(ConnectorConstant.OPERTION_QUERY_QUARTZ, sourceConfig, fileds, queryFilter);
        // 移除事件字段
        fileds.remove(map);
        String table = sourceConfig.getTable().toUpperCase();
        String quartzFiled = inc.get("quartzFiled");
        // 获取增量语句   LASTDATE > '2017-11-10 11:07:41' AND LASTDATE <= '2017-11-10 11:30:01'
        String jdbcQuartzRangeSQL = connector.getJdbcQuartzRangeSQL(table, quartzFiled, queryFilter);
        // 获取增量语句   LASTDATE <= '2017-11-10 11:07:41'
        String jdbcQuartzAllSQL = connector.getJdbcQuartzAllSQL(table, quartzFiled, queryFilter);
        // 获取最近时间 SELECT MAX(LASTDATE) FROM ASD_TEST;
        String jdbcQuartzMaxSQL = connector.getJdbcQuartzMaxSQL(table, quartzFiled);

        // 获取增量语句  SELECT ASD_TEST.ID, ASD_TEST.DATE FROM ASD_TEST WHERE (ID!='123')
        executeCommond.put(ConnectorConstant.OPERTION_QUERY_QUARTZ, sourceExecuteCommond);
        executeCommond.put(ConnectorConstant.OPERTION_QUERY_QUARTZ_RANGE, jdbcQuartzRangeSQL);
        executeCommond.put(ConnectorConstant.OPERTION_QUERY_QUARTZ_ALL, jdbcQuartzAllSQL);
        executeCommond.put(ConnectorConstant.OPERTION_QUERY_QUARTZ_MAX, jdbcQuartzMaxSQL);
        logger.info("sourceExecuteQueryQuartzCommond:" + sourceExecuteCommond);
        logger.info("sourceExecuteQueryQuartzRangeCommond:" + jdbcQuartzRangeSQL);
        logger.info("sourceExecuteQueryQuartzAllCommond:" + jdbcQuartzAllSQL);
        logger.info("sourceExecuteQueryQuartzMaxCommond:" + jdbcQuartzMaxSQL);

        // 获取最近一次增量记录点,2017-11-08 11:07:26
        if (StringUtils.isBlank(jdbcQuartzMaxSQL) || StringUtils.isNotBlank(inc.get("scnPos"))) {
            return;
        }
        JdbcTemplate jdbc = null;
        try {
            jdbc = connector.getJdbcTemplate(sourceConfig);
            Timestamp scnPos = jdbc.queryForObject(jdbcQuartzMaxSQL, Timestamp.class);
            inc.put("scnPos", String.valueOf(scnPos));
            logger.info("sourceExecuteQueryQuartzMaxCommond result:" + scnPos);
        } catch (DataAccessException e) {
            logger.error(e.getClass() + " > " + e.getLocalizedMessage());
        } finally {
            connector.close(jdbc);
        }
    }

    /**
     * 获取定时quartz增量语句
     * @param sourceConfig
     * @param po
     * @param executeCommond
     * @param sqlQuery 获取查询语句 SELECT T1.*,T2.*,T2.ID FROM USER T1 LEFT JOIN type T2 ON(T1.POST_ID = T2.ID)
     */
    private static void getQuartzExecuteCommond(Database connector, DatabaseConfig sourceConfig, Map<String, Map<String, String>> po, Map<String, String> executeCommond,
            String sqlQuery) {
        Map<String, String> inc = po.get(MappingConstant.POLICY_INCREMENT);
        if (null == inc) {
            return;
        }
        // 排除非定时策略
        String model = inc.get("model");
        if (!MappingConstant.POLICY_INCREMENT_MODEL_QUARTZ.equals(model)) {
            return;
        }

        // 检查是否包含where语法
        String where = sqlQuery.contains(" WHERE ") || sqlQuery.contains(" WHERE\n") || sqlQuery.contains("\nWHERE\n") ? " AND " : " WHERE ";
        String tableLabel = inc.get("tableLabel") + ".";
        String quartzFiled = inc.get("quartzFiled");
        // 获取增量语句   T1.LASTDATE > '2017-11-10 11:07:41' AND T1.LASTDATE <= '2017-11-10 11:30:01' ORDER BY T1.LASTDATE
        String jdbcQuartzRangeSQL = new StringBuilder().append(where).append(tableLabel).append(quartzFiled).append(" > ? AND ").append(tableLabel).append(quartzFiled)
                .append("<= ?").append(" ORDER BY ").append(tableLabel).append(quartzFiled).toString();

        // 获取增量语句   T1.LASTDATE >= '2017-11-10 11:07:41'
        String jdbcQuartzAllSQL = new StringBuilder().append(where).append(tableLabel).append(quartzFiled).append(" >= ?").toString();

        // 获取最近时间 SELECT MAX(T_MAX_.LASTDATE) FROM (SELECT * FROM TB ...) T_MAX_.;
        String jdbcQuartzMaxSQL = new StringBuilder().append("SELECT MAX(T_MAX_.").append(quartzFiled).append(") FROM (").append(sqlQuery).append(") T_MAX_").toString();

        // 获取增量语句  SELECT ASD_TEST.ID, ASD_TEST.DATE FROM ASD_TEST WHERE (ID!='123')
        executeCommond.put(ConnectorConstant.OPERTION_QUERY_QUARTZ, sqlQuery);
        executeCommond.put(ConnectorConstant.OPERTION_QUERY_QUARTZ_RANGE, jdbcQuartzRangeSQL);
        executeCommond.put(ConnectorConstant.OPERTION_QUERY_QUARTZ_ALL, jdbcQuartzAllSQL);
        executeCommond.put(ConnectorConstant.OPERTION_QUERY_QUARTZ_MAX, jdbcQuartzMaxSQL);
        logger.info("sourceExecuteQueryQuartzCommond:" + sqlQuery);
        logger.info("sourceExecuteQueryQuartzRangeCommond:" + jdbcQuartzRangeSQL);
        logger.info("sourceExecuteQueryQuartzAllCommond:" + jdbcQuartzAllSQL);
        logger.info("sourceExecuteQueryQuartzMaxCommond:" + jdbcQuartzMaxSQL);

        // 获取最近一次增量记录点,2017-11-08 11:07:26
        if (StringUtils.isBlank(jdbcQuartzMaxSQL.toString())) {
            return;
        }
        JdbcTemplate jdbc = null;
        try {
            jdbc = connector.getJdbcTemplate(sourceConfig);
            Timestamp scnPos = jdbc.queryForObject(jdbcQuartzMaxSQL, Timestamp.class);
            inc.put("scnPos", String.valueOf(scnPos));
            logger.info("sourceExecuteQueryQuartzMaxCommond result:" + scnPos);
        } catch (DataAccessException e) {
            logger.error(e.getClass() + " > " + e.getLocalizedMessage());
        } finally {
            connector.close(jdbc);
        }
    }

}
